cmake_minimum_required (VERSION 3.14...3.28)

# Avoid IPO/LTO Warnings:
cmake_policy(SET CMP0069 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)

# Allow overwriting options of external projects from this CMakeLists file
cmake_policy(SET CMP0077 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)

if ("${CMAKE_VERSION}" GREATER_EQUAL "3.21")
  # Allow overwriting cache variables of external projects from this CMakeLists file
  cmake_policy(SET CMP0126 NEW)
  set(CMAKE_POLICY_DEFAULT_CMP0126 NEW)
endif()

# Allow portable use of CMAKE_VISIBILITY_INLINES_HIDDEN not only for shared libraries
cmake_policy(SET CMP0063 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)

# Check if we build within the llvm source tree
if (DEFINED LLVM_MAIN_SRC_DIR)
  set(PHASAR_IN_TREE 1)
endif()

if (NOT PHASAR_IN_TREE)
  project (phasar
    LANGUAGES C CXX
    DESCRIPTION "A LLVM-based static analysis framework."
    VERSION 2510
  )
endif ()
set(PHASAR_VERSION ${PROJECT_VERSION})

# NOTE: When we require cmake >= 3.21, we can use PROJECT_IS_TOP_LEVEL instead
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
  set(PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT ON)
else()
  set(PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT OFF)
endif()

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

include(GNUInstallDirs)

set_property(GLOBAL PROPERTY TARGET_MESSAGES OFF)

option(PHASAR_ENABLE_SANITIZERS "Build PhASAR with AddressSanitizer and UBSanitizer (default is OFF)" OFF)

set(PHASAR_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(PHASAR_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PHASAR_SRC_DIR}/cmake")
include("phasar_macros")

if (NOT CMAKE_BUILD_TYPE AND NOT GENERATOR_IS_MULTI_CONFIG AND NOT PHASAR_IN_TREE)
  message(STATUS "No CMAKE_BUILD_TYPE specified, setting it to Debug")
  set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build mode ('Debug' or 'Release', default is 'Debug')" FORCE)
endif ()

if(GENERATOR_IS_MULTI_CONFIG)
  message(STATUS "Selected multi-config Build")
  set(CMAKE_CONFIGURATION_TYPES Debug RelWithDebInfo Release CACHE STRING "Configuration types: Debug, RelWithDebInfo and Release" FORCE)
else()
  message(STATUS "Selected ${CMAKE_BUILD_TYPE} Build")
endif()

set(DEBUG_CONFIGURATIONS DEBUG CACHE INTERNAL "" FORCE)
set(RELEASE_CONFIGURATIONS RELWITHDEBINFO RELEASE CACHE INTERNAL "" FORCE)

# TODO: Once available, we may want to use -fextend-lifetimes on Debug- and RelWithDebInfo builds to improve debugging experience
# https://reviews.llvm.org/D157613

string(APPEND CMAKE_CXX_FLAGS " -MP -fstack-protector-strong -ffunction-sections -fdata-sections -pipe")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS_RELEASE "")

option(CMAKE_VISIBILITY_INLINES_HIDDEN "Hide inlined functions from the DSO table (default ON)" ON)

option(PHASAR_BUILD_MODULES "Build C++20 modules for phasar" OFF)

include(CheckCXXCompilerFlag)

# Handle memory issues with linking
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
  string(APPEND CMAKE_CXX_FLAGS_DEBUG " -gsplit-dwarf")
  string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -gsplit-dwarf")
  set(LINKER_FLAGS_SAVE ${CMAKE_EXE_LINKER_FLAGS})

  # See LLVM_USE_SPLIT_DWARF in LLVM
  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gdb-index")
  cmake_policy(PUSH)
  cmake_policy(SET CMP0056 NEW)
  check_cxx_compiler_flag("" GDB_INDEX_SUPPORTED)
  cmake_policy(POP)
  set(CMAKE_EXE_LINKER_FLAGS ${LINKER_FLAGS_SAVE})

  if(GDB_INDEX_SUPPORTED)
    link_libraries(debug "-Wl,--gdb-index")
  endif()
endif()

# march=native

# NOTE: Use gcc -march=native -Q --help=target | grep -- '-march=' | cut -f3
#       to check the architecture detected by match=native
# set(PHASAR_TARGET_ARCH "" CACHE STRING "Optimize the build for the given target architecture, e.g. -march=native. Most useful in Release builds. Disabled by default")

if (DEFINED PHASAR_TARGET_ARCH)
  if (NOT CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT GENERATOR_IS_MULTI_CONFIG)
    message(WARNING "The PHASAR_TARGET_ARCH flag will be ignored in non-Release build type ${CMAKE_BUILD_TYPE}")
  else()
    set(PHASAR_TARGET_ARCH_INTERNAL "${PHASAR_TARGET_ARCH}")
  endif()
elseif("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
  set(PHASAR_TARGET_ARCH_INTERNAL "native")
endif()

if (NOT "${PHASAR_TARGET_ARCH_INTERNAL}" STREQUAL "")
  check_cxx_compiler_flag("-march=${PHASAR_TARGET_ARCH_INTERNAL}" MARCH_SUPPORTED)
  if (MARCH_SUPPORTED)
    message(STATUS "Target architecture '${PHASAR_TARGET_ARCH_INTERNAL}' enabled")
    string(APPEND CMAKE_CXX_FLAGS_RELEASE " -march=${PHASAR_TARGET_ARCH_INTERNAL}")
  else()
    message(WARNING "Target architecture '${PHASAR_TARGET_ARCH_INTERNAL}' not supported. Fallback to generic build")
  endif()
endif()

# Sanitizers
if (PHASAR_ENABLE_SANITIZERS)
  message(STATUS "Selected ${CMAKE_BUILD_TYPE} Build with Sanitizers")

  if(MSVC)
    set(ASAN_FLAG "/fsanitize=address")
  else()
    set(ASAN_FLAG "-fsanitize=address,undefined")
  endif()

  string(APPEND CMAKE_CXX_FLAGS " ${ASAN_FLAG}")
endif()

# LTO
option(PHASAR_ALLOW_LTO_IN_RELEASE_BUILD "Use link-time-optimization (LTO) in Release builds, if possible (default is ON)" ON)
if(PHASAR_ALLOW_LTO_IN_RELEASE_BUILD)
  if (GENERATOR_IS_MULTI_CONFIG OR CMAKE_BUILD_TYPE STREQUAL "Release")
    include(CheckIPOSupported)
    check_ipo_supported(RESULT LTO_SUPPORTED OUTPUT LTO_SUPPORT_ERROR)

    if(LTO_SUPPORTED)
      message(STATUS "IPO/LTO enabled in Release mode")
      set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON) # LTO
    else()
      message(STATUS "IPO/LTO not supported: ${LTO_SUPPORT_ERROR}")
    endif()
  endif()
endif()

option(PHASAR_BUILD_UNITTESTS "Build all tests (default is ON)" ${PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT})

option(PHASAR_BUILD_OPENSSL_TS_UNITTESTS "Build OPENSSL typestate tests (require OpenSSL, default is OFF)" OFF)

option(PHASAR_USE_Z3 "Build the phasar_llvm_pathsensitivity library with Z3 support for constraint solving (default is OFF)" OFF)

option(PHASAR_BUILD_IR "Build IR test code (default is ON)" ${PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT})

option(PHASAR_ENABLE_CLANG_TIDY_DURING_BUILD "Run clang-tidy during build (default is OFF)" OFF)

option(PHASAR_BUILD_DOC "Build documentation" OFF)

option(PHASAR_DEBUG_LIBDEPS "Debug internal library dependencies (private linkage)" OFF)

option(PHASAR_BUILD_TOOLS "Build PhASAR-based tools (default is ON)" ${PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT})
option(PHASAR_USE_CONAN "Using Conan for dependencies instead of submodules." OFF)

#option(BUILD_SHARED_LIBS "Build shared libraries (default is ON)" ON)
option(PHASAR_BUILD_DYNLIB "Build one fat shared library. Requires BUILD_SHARED_LIBS to be turned OFF (default is OFF)" OFF)

if(PHASAR_BUILD_DYNLIB AND BUILD_SHARED_LIBS)
  message(FATAL_ERROR "PHASAR_BUILD_DYNLIB is incompatible with BUILD_SHARED_LIBS")
endif()

if (PHASAR_DEBUG_LIBDEPS)
  if (NOT BUILD_SHARED_LIBS)
    set(BUILD_SHARED_LIBS ON)
    message("PHASAR_DEBUG_LIBDEPS only works for shared libraries, so set BUILD_SHARED_LIBS=ON")
  endif()
  if (PHASAR_ENABLE_SANITIZERS)
    message(FATAL_ERROR "PHASAR_DEBUG_LIBDEPS is incompatible with ASAN (see https://clang.llvm.org/docs/AddressSanitizer.html#usage)")
  endif()
endif ()

option(PHASAR_ENABLE_PIC "Build Position-Independed Code" ON)
if (PHASAR_ENABLE_PIC)
  set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif (PHASAR_ENABLE_PIC)

# PAMM
if (NOT PHASAR_ENABLE_PAMM)
  set(PHASAR_ENABLE_PAMM "Off" CACHE STRING "Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is 'Off')" FORCE)
  set_property(CACHE PHASAR_ENABLE_PAMM PROPERTY STRINGS "Off" "Core" "Full")
endif()
if(PHASAR_ENABLE_PAMM STREQUAL "Core" AND NOT PHASAR_BUILD_UNITTESTS)
  set(PAMM_CORE ON)
  message(STATUS "PAMM metric severity level: Core")
elseif(PHASAR_ENABLE_PAMM STREQUAL "Full" AND NOT PHASAR_BUILD_UNITTESTS)
  set(PAMM_FULL ON)
  message(STATUS "PAMM metric severity level: Full")
elseif(PHASAR_BUILD_UNITTESTS AND (PHASAR_ENABLE_PAMM STREQUAL "Core" OR PHASAR_ENABLE_PAMM STREQUAL "Full"))
  message(WARNING "PAMM metric severity level: Off (due to unittests)")
else()
  message(STATUS "PAMM metric severity level: Off")
endif()

# Logger
option(PHASAR_ENABLE_DYNAMIC_LOG "Makes it possible to switch the logger on and off at runtime; otherwise, it is turned off at compile-time (default is ON)" ON)

if (PHASAR_ENABLE_DYNAMIC_LOG)
  message(STATUS "Dynamic log enabled")
  set(DYNAMIC_LOG ON)
else()
  message(STATUS "Dynamic log disabled")
endif()

if (PHASAR_USE_CONAN)
elseif (NOT PHASAR_IN_TREE)
  # RPATH
  set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})

  if (NOT "${CMAKE_INSTALL_LIBDIR}" STREQUAL "lib")
    message(STATUS "Detected CMAKE_INSTALL_LIBDIR that deviates from 'lib': ${CMAKE_INSTALL_LIBDIR}. Add ${CMAKE_INSTALL_PREFIX}/lib to the RPATH as json-schema-validator needs it")
    list(APPEND CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)
  endif()

  set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

  # Export set
  set(PHASAR_DEPS_EXPORT_SET PhasarDepsExports)
else()
  # Export set
  set(PHASAR_DEPS_EXPORT_SET LLVMExports)
endif()

set(PHASAR_DEPS_INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/phasar/deps)

# Filesystem

# Check if the underlying toolchain has a default std filesystem header
include(CheckIncludeFileCXX)
check_include_file_cxx(filesystem HAS_FILESYSTEM_HEADER)

if (NOT HAS_FILESYSTEM_HEADER)
  # If no file system exists, link a filesystem explicitly
  if (LLVM_ENABLE_LIBCXX)
    set(PHASAR_STD_FILESYSTEM c++fs)
  else()
    set(PHASAR_STD_FILESYSTEM stdc++fs)
  endif()
endif()

# Config
set(PHASAR_CUSTOM_CONFIG_INSTALL_DIR "" CACHE STRING "If set, customizes the directory, where configuration files for PhASAR are installed (default is ${CMAKE_INSTALL_PREFIX}/.phasar-config)")
if ("${PHASAR_CUSTOM_CONFIG_INSTALL_DIR}" STREQUAL "")
  set(PHASAR_CONFIG_INSTALL_DIR ".phasar-config/")
else()
  set(PHASAR_CONFIG_INSTALL_DIR "${PHASAR_CUSTOM_CONFIG_INSTALL_DIR}")
endif()

### Adding external libraries

# Threads
find_package(Threads)

# Disable clang-tidy for the external projects
set(CMAKE_CXX_CLANG_TIDY "")

# Nlohmann JSON
include(add_nlohmann_json)
add_nlohmann_json()
add_json_schema_validator()

# Coverage
if (CODE_COVERAGE)
  set(CODE_COVERAGE_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/code-coverage.cmake")
  file(DOWNLOAD "https://raw.githubusercontent.com/StableCoder/cmake-scripts/refs/tags/25.08/code-coverage.cmake" "${CODE_COVERAGE_SCRIPT}"
    EXPECTED_HASH SHA256=cad4dd2bdf944f77627687e0298debc8341bfdeffaf949937f319c7c4ff3fdaf
    TLS_VERIFY ON
    STATUS CODE_COVERAGE_SCRIPT_DOWNLOAD_STATUS
  )
  message(STATUS "Download code-coverage.cmake: ${CODE_COVERAGE_SCRIPT_DOWNLOAD_STATUS}")

  if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    string(REGEX MATCH "^[0-9]+" COMPILER_VERSION_MAJOR "${CMAKE_CXX_COMPILER_VERSION}")
    message(STATUS "Clang major version: ${COMPILER_VERSION_MAJOR}")
    set(LLVM_TOOL_VERSION_SUFFIX "-${COMPILER_VERSION_MAJOR}")
  endif()

  # pre-populate these cmake variables as we require specific versions:
  find_program(LLVM_COV_PATH "llvm-cov${LLVM_TOOL_VERSION_SUFFIX}" REQUIRED)
  find_program(LLVM_PROFDATA_PATH "llvm-profdata${LLVM_TOOL_VERSION_SUFFIX}" REQUIRED)
  message(STATUS "Found llvm-cov: ${LLVM_COV_PATH}")
  message(STATUS "Found llvm-profdata: ${LLVM_PROFDATA_PATH}")

  include("${CODE_COVERAGE_SCRIPT}")

  find_program(cxxfilt "c++filt" QUIET)
  if (cxxfilt)
    set(DEMANGLER_OPTIONS LLVM_COV_HTML_OPTIONS "-Xdemangler=${cxxfilt}")
  endif()

  set(COV_FILEPATH_FILTER "(usr/*|external/|unittests/)")
  add_code_coverage_all_targets(EXCLUDE ${COV_FILEPATH_FILTER} ${DEMANGLER_OPTIONS})
endif()

# Googletest
if (NOT PHASAR_IN_TREE)
  if(PHASAR_BUILD_UNITTESTS AND NOT TARGET gtest)
    include(FetchContent)

    set(INSTALL_GTEST OFF)
    FetchContent_Declare(
        googletest
        GIT_REPOSITORY https://github.com/google/googletest.git
        GIT_TAG v1.17.0
    )
    FetchContent_MakeAvailable(googletest)
  endif()
else()
  # Set llvm distributed includes for gtest header
  set(GTEST_INCLUDE_DIR "${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include")
endif()

option(USE_LLVM_FAT_LIB  "Link against libLLVM.so instead of the individual LLVM libraries if possible (default is OFF; always on if BUILD_SHARED_LIBS is ON)" OFF)

# LLVM
if (NOT PHASAR_LLVM_VERSION)
  set(PHASAR_LLVM_VERSION 15)
endif()
include(add_llvm)
add_llvm()

# SVF
option(PHASAR_USE_SVF "Use SVF for more options in alias analysis (default is OFF)" OFF)
if(PHASAR_USE_SVF)
  find_package(SVF REQUIRED CONFIG)
  message(STATUS "Found SVF ${SVF_VERSION}")

  if (NOT PHASAR_USE_Z3)
    message(WARNING "SVF requires Z3. Set PHASAR_USE_Z3=ON")
    set(PHASAR_USE_Z3 ON)
  endif()
endif()

# Z3 Solver
if(PHASAR_IN_TREE)
  set (PHASAR_USE_Z3 OFF)
else()
  if(PHASAR_USE_Z3)
    # This z3-version is the same version LLVM requires; however, we cannot just use Z3 via the LLVM interface
    # as it lacks some functionality (such as z3::expr::simplify()) that we require

    # FindZ3.cmake by llvm tries to compile a snippet with Z3 which crashes on arm with sanitizers enabled
    set(SAFE_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
    set(CMAKE_CXX_FLAGS "")
    find_package(Z3 4.7.1 REQUIRED)
    set(CMAKE_CXX_FLAGS "${SAFE_CMAKE_CXX_FLAGS}")

    if(Z3_FOUND)
      if (NOT TARGET z3)
        add_library(z3 IMPORTED SHARED)
        set_property(TARGET z3 PROPERTY
                IMPORTED_LOCATION ${Z3_LIBRARIES})
        set_property(TARGET z3 PROPERTY
                INTERFACE_INCLUDE_DIRECTORIES ${Z3_INCLUDE_DIR})
      endif()
    endif()
  endif()
endif()

# Clang
option(BUILD_PHASAR_CLANG "Build the phasar_clang library (default is OFF)" OFF)
if(BUILD_PHASAR_CLANG)
  add_clang()
endif()

# Set up clang-tidy to run during PhASAR's compilation to indicate code smells
if (PHASAR_ENABLE_CLANG_TIDY_DURING_BUILD)
  message(STATUS "Enabled clang-tidy during build")
  set(CMAKE_CXX_CLANG_TIDY
    clang-tidy;
    -header-filter=include/phasar.*h$;
    # -warnings-as-errors=*;
  )
endif ()

# Library Dependency Dirs
if(NOT PHASAR_IN_TREE)
  separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
  add_definitions(${LLVM_DEFINITIONS_LIST})
endif()

# Warnings
option(PHASAR_ENABLE_WARNINGS "Enable warnings" ${PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT})
if (PHASAR_ENABLE_WARNINGS)
  if (MSVC)
    string(APPEND CMAKE_CXX_FLAGS " /W4")
  else()
    string(APPEND CMAKE_CXX_FLAGS " -Wall -Wextra -Wno-unused-parameter")
  endif()
endif (PHASAR_ENABLE_WARNINGS)


# Headers
add_library(phasar_interface INTERFACE)
file(GLOB_RECURSE PHASAR_PUBLIC_HEADERS include/*.h include/*.def)
configure_file(config.h.in include/phasar/Config/phasar-config.h @ONLY)

target_sources(phasar_interface INTERFACE
  FILE_SET HEADERS
  BASE_DIRS "${PHASAR_SRC_DIR}/include" "${PHASAR_BINARY_DIR}/include"
  FILES ${PHASAR_PUBLIC_HEADERS} "${PHASAR_BINARY_DIR}/include/phasar/Config/phasar-config.h"
)

# Some preprocessor symbols that need to be available in phasar sources, but should not be installed
add_cxx_compile_definitions(PHASAR_SRC_DIR="${CMAKE_SOURCE_DIR}")
add_cxx_compile_definitions(PHASAR_BUILD_DIR="${CMAKE_BINARY_DIR}")

# Add PhASAR's subdirectories
add_subdirectory(lib)

# phasar-based binaries
if(PHASAR_BUILD_TOOLS)
  add_subdirectory(tools)
endif()

# Swift tests
option(BUILD_SWIFT_TESTS "Builds the Swift tests (Swift compiler has to be installed manually beforehand!)" OFF)
if (BUILD_SWIFT_TESTS)
  set(CMAKE_Swift_FLAGS_RELEASE "-g")
  set(CMAKE_Swift_FLAGS_RELWITHDEBINFO "-g")
  enable_language(Swift)
endif(BUILD_SWIFT_TESTS)

# Add Phasar unittests and build all IR test code
if (PHASAR_BUILD_UNITTESTS)
  message("Phasar unittests")

  enable_testing()
  add_subdirectory(unittests)
  if(NOT PHASAR_BUILD_IR)
    message(WARNING "Set PHASAR_BUILD_IR=ON, because PHASAR_BUILD_UNITTESTS is ON")
    set(PHASAR_BUILD_IR ON)
  endif()
endif()

# Build all IR test code
if (PHASAR_BUILD_IR)
  message("Building IR test code")
  add_subdirectory(test)
endif()

set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}" CACHE PATH "Install dir of headers")
set(LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}" CACHE PATH "Install dir of libraries")

# Install targets of phasar-cli, other executables, and libraries are to be
# found in the individual subdirectories of tools/

if(NOT PHASAR_IN_TREE)
  install(TARGETS phasar_interface
    EXPORT PhasarExports
    FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
  )
  if (NOT PHASAR_USE_CONAN)
    # Install the export-set containing all the phasar targets
    install(EXPORT PhasarExports
      FILE PhasarExports.cmake
      NAMESPACE phasar::
      DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/phasar"
    )
    install(EXPORT ${PHASAR_DEPS_EXPORT_SET}
      FILE ${PHASAR_DEPS_EXPORT_SET}.cmake
      NAMESPACE phasar::deps::
      DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/phasar"
    )
  endif()
else()
  install(TARGETS phasar_interface
    EXPORT LLVMExports
    FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
  )
  set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS phasar_interface)
endif()

# Install Phasar utils helper scripts
install(DIRECTORY utils/
  DESTINATION bin
  FILES_MATCHING
  PATTERN "CodeGen" EXCLUDE # CodeGen does not contain files to install
  PATTERN "conan" EXCLUDE   # Don't install conan utils
  PATTERN "phasar-*"
  PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
              GROUP_EXECUTE GROUP_READ
              WORLD_EXECUTE WORLD_READ
)

# Install the Phasar config files into CMAKE_INSTALL_PREFIX/.phasar-config/
install(DIRECTORY config/
  DESTINATION ${PHASAR_CONFIG_INSTALL_DIR}
  PATTERN "config/*"
  PERMISSIONS OWNER_WRITE OWNER_READ
              GROUP_WRITE GROUP_READ
              WORLD_READ
)

include(CMakePackageConfigHelpers)
configure_package_config_file(
  Config.cmake.in
  phasarConfig.cmake
  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/phasar
  PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR
)

write_basic_package_version_file(
  ${CMAKE_CURRENT_BINARY_DIR}/phasarConfigVersion.cmake
  VERSION ${PHASAR_VERSION}
  COMPATIBILITY SameMajorVersion
)

### Install Config and ConfigVersion files
install(
  FILES "${CMAKE_CURRENT_BINARY_DIR}/phasarConfig.cmake"
        "${CMAKE_CURRENT_BINARY_DIR}/phasarConfigVersion.cmake"
  DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/phasar"
)

# If the Phasar shared object libraries are not installed into a system folder
# the so libs must be added manually to the linker search path and the linker
# config must be updated as follows:
#
#   $ export LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib:/home/philipp/Schreibtisch/tmp/lib
#   $ sudo ldconfig
#
# Or even better: just link statically when trying to package Phasar <- this is no longer possible

# Settings for building various packages using Cpack
#   How to pack using the following settings?
#     $ mkdir build
#     $ cd build
#     $ cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/local ..
#     $ cpack ..
#     $ dpkg -i ./<the_package>.deb or better: apt-get install ./<the_package>.deb
set(MAJOR_VERSION 1)
set(MINOR_VERSION 0)
set(PATCH_VERSION 0)
if (NOT PHASAR_IN_TREE)
  if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
    include(InstallRequiredSystemLibraries)
    set(CPACK_SET_DESTDIR "on")
    set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")

    set(CPACK_GENERATOR "DEB")
    set(CPACK_PACKAGE_DESCRIPTION "Phasar")
    set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Phasar a LLVM-based static analysis framework")
    set(CPACK_PACKAGE_VENDOR "Phasar Team - Philipp Schubert and others")
    set(CPACK_PACKAGE_CONTACT "philipp.schubert@upb.de")
    set(CPACK_PACKAGE_VERSION_MAJOR "${MAJOR_VERSION}")
    set(CPACK_PACKAGE_VERSION_MINOR "${MINOR_VERSION}")
    set(CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}")
    set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}")
    set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}")
    # package dependencies can be set-up here
    # better use autogenerated dependency information
    set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
    set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
    set(CPACK_DEBIAN_PACKAGE_SECTION "kde")
    set(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
    set(CPACK_COMPONENTS_ALL Libraries ApplicationData)
    include(CPack)
  endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
endif()

# Setup the doxygen code documentation
if(PHASAR_BUILD_DOC)
  find_package(Doxygen REQUIRED)

  set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in)
  set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
  configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)

  add_custom_target(doc_doxygen ALL
    COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
    COMMENT "Generating API documentation with Doxygen"
    VERBATIM
  )
endif()
